﻿using System;
using System.Collections.Generic;
using Blockey.GameObjects3D;
using Blockey.MenuSystemScreens;
using Blockey.ScreenManagers;
using BlockeyEssentials;
using BlockeyEssentials.Input;
using BlockeyEssentials.ScreenSystem;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using System.Linq;

namespace Blockey.GameplayScreens
{
    public abstract class MatchScreen3D : Screen
    {
        public MatchScreen3D(ScreenManager oManager)
            : base(oManager)
        {
            var oGraphics = oManager.Game.GraphicsDevice;
            var oGame = oManager.Game;

            m_oCamera = new Camera3D();
            m_oGrid = new Grid3D();
            m_oRink = new Rink(oGraphics, oGame.Content);
            m_oArena = new Arena(oGraphics, oGame.Content);

            m_oCustomModels = new List<DrawableCustom>();
            m_oPeople = new List<Person3D>();
            m_oMoveables = new List<Moveable3D>();
            m_oModels = new List<DrawableModel>();

            m_oControllingPlayers = new List<ControllingPlayer>();

            AddObject(m_oArena);

            m_bCameraMode = false;
            DebugShapeRenderer.ShowLines = false;

            if (Networking.IsRunning == false)
            {
                InsertSkater(oGame, ControllingPlayer.PlayerOne, Alliance.Home);
                //InsertSkater(oGame, ControllingPlayer.AI, Alliance.Home);
                //InsertSkater(oGame, ControllingPlayer.AI, Alliance.Away);
                //InsertSkater(oGame, ControllingPlayer.AI, Alliance.Away);
            }

            _PlayerIndicator = new DrawableModel(oGraphics, oManager.Game.Content, "Models\\Pyramid\\Pyramid", false);
            _PlayerIndicatorRotation = 0.0f;

            m_oHighlightSkater = null;

            m_oPuck = new Puck3D(oGame.GraphicsDevice, oGame.Content);

            m_oHomeGoal = new Goal3D(oGame.GraphicsDevice, oGame.Content, false);
            m_oAwayGoal = new Goal3D(oGame.GraphicsDevice, oGame.Content, true);

            AddObject(m_oPuck);
            AddObject(m_oHomeGoal);
            AddObject(m_oAwayGoal);

            m_oHomeGoalie = new Goalie(oGame, ControllingPlayer.AI, Alliance.Home, m_oHomeGoal);
            m_oAwayGoalie = new Goalie(oGame, ControllingPlayer.AI, Alliance.Away, m_oAwayGoal);

            AddObject(m_oHomeGoalie);
            AddObject(m_oAwayGoalie);

            SetUpFaceOffPositions();

            HockeyMatchState = GameState.Playing;
        }

        public override void Update()
        {
            #region Debug Helpers
            if (InputManager.Keyboard.JustPressed(Keys.Q) || InputManager.GamePad1.JustPressed(Buttons.LeftShoulder))
                m_bCameraMode = !m_bCameraMode;
            if (InputManager.Keyboard.JustPressed(Keys.W) || InputManager.GamePad1.JustPressed(Buttons.RightShoulder))
                DebugShapeRenderer.ShowLines = !DebugShapeRenderer.ShowLines;

            if (InputManager.Keyboard.JustPressed(Keys.D1))
                HockeyMatchState = GameState.Introduction;
            if (InputManager.Keyboard.JustPressed(Keys.D2))
                HockeyMatchState = GameState.FaceOff;
            if (InputManager.Keyboard.JustPressed(Keys.D3))
                HockeyMatchState = GameState.GoalScoredHome;
            if (InputManager.Keyboard.JustPressed(Keys.D4))
                HockeyMatchState = GameState.GoalScoredAway;
            if (InputManager.Keyboard.JustPressed(Keys.D5))
                HockeyMatchState = GameState.Intermission;
            if (InputManager.Keyboard.JustPressed(Keys.D6))
                HockeyMatchState = GameState.Conclusion;
            if (InputManager.Keyboard.JustPressed(Keys.D7))
                HockeyMatchState = GameState.Playing;

            #endregion

            #region Retrieve Online Information
            if (Networking.IsRunning)
            {
                if (Networking.Session.IsHost)
                {
                    HostReceiveFromClients();
                }
                else
                {
                    ClientReceiveFromHost();
                }
            }
            #endregion

            #region Camera Mode
            if (m_bCameraMode)
            {
                m_oCamera.Input();
            }
            #endregion

            #region Normal Update
            else
            {

                #region Online Game
                if (Networking.IsRunning)
                {

                    #region Host Actions
                    if (Networking.Session.IsHost)
                    {
                        foreach (var oGamer in Networking.Session.LocalGamers)
                        {
                            var oGamerPerson = oGamer.Tag as Person3D;
                            if (oGamerPerson != null)
                            {
                                if (m_bCameraMode == false)
                                    oGamerPerson.Input(this);
                            }
                        }

                        foreach (var oPerson in m_oPeople)
                        {
                            oPerson.UpdateRotation(false, false);
                        }
                    }
                    #endregion

                    #region Client Actions
                    else
                    {
                        foreach (var oGamer in Networking.Session.LocalGamers)
                        {
                            var oGamerPerson = oGamer.Tag as Person3D;
                            if (oGamerPerson != null)
                                oGamerPerson.Input(this);
                        }

                        foreach (var oPerson in m_oPeople)
                        {
                            oPerson.UpdateRotation(true, false);
                        }
                    }
                    #endregion

                    #region Shared Actions
                    MoveMoveables();
                    ArenaCollision();

                    //if (Networking.Session.IsHost)
                    //{
                    MoveableCollision();
                    //}
                    #endregion

                }
                #endregion

                #region Offline Game
                else
                {
                    if (HaltInput == false)
                    {
                        foreach (var oPerson in m_oPeople)
                        {
                            oPerson.Input(this);
                            oPerson.UpdateRotation(false, false);
                        }
                    }


                    switch (HockeyMatchState)
                    {
                        case GameState.Introduction:
                        case GameState.GoalScoredHome:
                        case GameState.GoalScoredAway:
                        case GameState.FaceOff:
                        case GameState.Intermission:
                        case GameState.Conclusion:
                        case GameState.Playing:
                            {
                                break;
                            }
                    }

                    UpdateConditions();
                    MoveMoveables();

                    if (DetectCollision)
                    {
                        ArenaCollision();
                        MoveableCollision();
                    }
                }
                #endregion

            }
            #endregion

            #region Send Online Information
            if (Networking.IsRunning)
            {
                if (Networking.Session.IsHost)
                {
                    SendHostToClients();
                }
                else
                {
                    SendClientToHost();
                }
                Networking.SendData();
            }
            #endregion

            #region Update Animations
            _PlayerIndicatorRotation += INDICATOR_ROTATION_RATE;
            #endregion

            #region Update Camera
            if (m_bCameraMode == false)
            {
                var oTarget = Vector3.Zero;
                var oCount = 0;
                foreach (var oGamer in m_oPeople)
                {
                    if (oGamer != null && oGamer.ControllingPlayer.IsAI == false)
                    {
                        oTarget += oGamer.Position;
                        oCount++;
                    }
                }
                if (oCount > 0)
                {
                    oTarget /= oCount;
                    oTarget.Y = m_oCamera.Target.Y;
                    oTarget.X *= 0.9f;
                    oTarget.X = MathHelper.Clamp(oTarget.X, -17.6f, 17.6f);
                    oTarget.Z = 0;

                    if (m_bCameraMode == false)
                        m_oCamera.SetTarget(oTarget);
                }
            }

            m_oCamera.Update();
            #endregion

            #region Puck Update

            m_oPuckHolder = null;

            // Determines if somebody is holding puck
            foreach (var oPerson in m_oSkaters)
            {
                if (oPerson.HeldObjects.Contains(m_oPuck))
                {
                    m_oPuckHolder = oPerson;
                    break;
                }
            }

            // Determines if somebody is picking up the puck.
            if (m_oPuckHolder == null)
            {
                foreach (var oPerson in m_oSkaters)
                {
                    if (oPerson.Condition.IsNeutral && m_oPuck.CollidesWith(oPerson))
                    {
                        if (oPerson.ControllingPlayer.IsAI)
                        {
                            List<Moveable3D> oBuddies = new List<Moveable3D>();
                            foreach (var oBuddy in m_oSkaters)
                            {
                                if (oPerson.TeamSide == oBuddy.TeamSide && oBuddy.ControllingPlayer.IsAI == false)
                                    oBuddies.Add(oBuddy);
                            }

                            var oClosest = (Person3D)Moveable3D.FindClosest(oPerson, oBuddies);
                            if (oClosest != null)
                            {
                                oPerson.SwitchPlayersWith(oClosest);
                            }
                        }

                        m_oPuckHolder = oPerson;
                        m_oPuckHolder.HeldObjects.Add(m_oPuck);
                        break;
                    }
                }
            }
            else
            {
                m_oPuck.Velocity = Vector3.Zero;
                var oAngle = new Vector3(All.AngleToVector(m_oPuckHolder.CurrentRotation.Z + MathHelper.Pi), 0);
                m_oPuck.Position = m_oPuckHolder.Position + (oAngle * (m_oPuckHolder.Radius + m_oPuck.Radius) * 1.25f);
            }

            #endregion
        }

        #region Draw Methods

        public override void Draw(SpriteBatch oSB)
        {
            m_oGrid.Draw(oSB.GraphicsDevice, m_oCamera);

            if (m_bCameraMode)
            {
                All.DrawDebugText("Camera Mode", new Vector2(600, 100), true);
            }
            if (DebugShapeRenderer.ShowLines)
            {
                All.DrawDebugText("Debug View", new Vector2(600, 130), true);
            }

            foreach (var oPerson in m_oPeople)
            {
                oPerson.Draw(oSB.GraphicsDevice, m_oCamera);

                if (DebugShapeRenderer.ShowLines)
                {
                    foreach (var oSphere in oPerson.Spheres)
                    {
                        DebugShapeRenderer.AddBoundingSphere(oSphere, Color.Blue);
                    }
                }
            }


            m_oRink.Draw(oSB.GraphicsDevice, m_oCamera);

            foreach (var oModel in m_oModels)
            {
                oModel.Draw(oSB.GraphicsDevice, m_oCamera);
            }

            var oDepth = oSB.GraphicsDevice.DepthStencilState;
            var oBlendstate = oSB.GraphicsDevice.BlendState;
            oSB.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            oSB.GraphicsDevice.BlendState = BlendState.AlphaBlend;

            foreach (var oMoveable in m_oMoveables)
            {
                oMoveable.DrawShadows(oSB.GraphicsDevice, m_oCamera);
            }
            oSB.GraphicsDevice.DepthStencilState = oDepth;
            oSB.GraphicsDevice.BlendState = oBlendstate;

            m_oPuck.Draw(oSB.GraphicsDevice, Camera);

            DrawIndicator(oSB);


            if (DebugShapeRenderer.ShowLines)
            {
                Color oDebugColor = Color.Purple;
                DebugShapeRenderer.AddBoundingBox(m_oRink.BoundingBox, oDebugColor);
                DebugShapeRenderer.AddBoundingCircle(m_oRink.BoundingSphereLD, oDebugColor);
                DebugShapeRenderer.AddBoundingCircle(m_oRink.BoundingSphereLU, oDebugColor);
                DebugShapeRenderer.AddBoundingCircle(m_oRink.BoundingSphereRD, oDebugColor);
                DebugShapeRenderer.AddBoundingCircle(m_oRink.BoundingSphereRU, oDebugColor);
            }

            DebugShapeRenderer.Draw(1 / 60f, m_oCamera.View, m_oCamera.Projection);
        }

        private void DrawIndicator(SpriteBatch oSB)
        {
            if (Networking.IsRunning)
            {
                int count = 0;
                foreach (var oGamer in Networking.Session.LocalGamers)
                {
                    var oPerson = oGamer.Tag as Person3D;
                    if (oPerson != null)
                    {
                        Color oColor;
                        switch (count)
                        {
                            case 0: { oColor = Color.Red; break; }
                            case 1: { oColor = Color.Blue; break; }
                            case 2: { oColor = Color.Green; break; }
                            case 3: { oColor = Color.Orange; break; }
                            default: { oColor = Color.Black; break; }
                        }
                        count++;

                        var oRotation = _PlayerIndicator.Rotation;
                        oRotation.Z = _PlayerIndicatorRotation;
                        _PlayerIndicator.SetTransformation(oPerson.Position + Vector3.UnitZ * 7, oRotation, 0.15f);
                        _PlayerIndicator.Draw(oSB.GraphicsDevice, m_oCamera);
                    }
                }
            }
            else
            {
                int count = 0;
                foreach (var oPerson in m_oPeople)
                {
                    if (oPerson.ControllingPlayer.IsAI) continue;

                    Color oColor;

                    switch (count)
                    {
                        case 0: { oColor = Color.Red; break; }
                        case 1: { oColor = Color.Blue; break; }
                        case 2: { oColor = Color.Green; break; }
                        case 3: { oColor = Color.Orange; break; }
                        default: { oColor = Color.Black; break; }
                    }
                    count++;

                    var oRotation = _PlayerIndicator.Rotation;
                    oRotation.Z = _PlayerIndicatorRotation;
                    _PlayerIndicator.SetTransformation(oPerson.Position + Vector3.UnitZ * 7, oRotation, 0.15f);
                    _PlayerIndicator.Draw(oSB.GraphicsDevice, m_oCamera);
                }
            }

            All.DrawDebugText(HockeyMatchState.ToString(), Vector2.Zero, true);
        }

        #endregion

        #region Networking Methods

        protected virtual void SendHostToClients()
        {
            var oWriter = Networking.Writer;

            foreach (var oGamer in Networking.Session.AllGamers)
            {
                var oPerson = oGamer.Tag as Person3D;
                if (oPerson != null)
                {
                    var oPositionDestination = oPerson.Position + oPerson.Velocity * Networking.FrameSkip;
                    oWriter.Write((short)(100 * oPositionDestination.X));
                    oWriter.Write((short)(100 * oPositionDestination.Y));
                    oWriter.Write((short)MathHelper.ToDegrees(oPerson.Rotation.Z));
                    oWriter.Write((byte)oPerson.Condition.Name);
                }
            }
        }

        protected virtual void SendClientToHost()
        {
            var oWriter = Networking.Writer;

            foreach (var oGamer in Networking.Session.LocalGamers)
            {
                var oPerson = oGamer.Tag as Person3D;
                if (oPerson != null)
                {
                    var oAngle = 0.0f;
                    var oByte = (byte)0;
                    var oDirection = new Vector2(oPerson.Direction.X, oPerson.Direction.Y);

                    if (oDirection != Vector2.Zero)
                    {
                        oAngle = MathHelper.ToDegrees(All.VectorToAngle(oDirection)) + 180;
                        oByte = (byte)(Math.Abs(254f * (oAngle / 360f)) + 1);
                    }

                    oWriter.Write(oByte);

                    byte oStatus = 0;
                    if (oPerson.PressingA)
                        oStatus += 1;
                    if (oPerson.PressingB)
                        oStatus += 2;
                    if (oPerson.PressingX)
                        oStatus += 4;
                    if (oPerson.PressingY)
                        oStatus += 8;

                    oWriter.Write(oStatus);
                }
            }
        }

        protected virtual void HostReceiveFromClients()
        {
            foreach (var oLocal in Networking.Session.LocalGamers)
            {
                if (oLocal.IsHost)
                {
                    while (oLocal.IsDataAvailable)
                    {
                        NetworkGamer oGamer;
                        oLocal.ReceiveData(Networking.Reader, out oGamer);

                        if (oGamer == oLocal)
                            continue;

                        var oPerson = oGamer.Tag as Person3D;
                        if (oPerson != null)
                        {
                            var oBytes = Networking.Reader.ReadByte();
                            var oDirection = Vector2.Zero;

                            if (oBytes != 0)
                            {
                                oBytes -= 1;
                                var oAngle = (float)oBytes / (float)254;
                                oAngle *= 360;
                                oAngle -= 90;
                                oAngle = MathHelper.ToRadians(oAngle);
                                oDirection = All.AngleToVector(oAngle);
                            }

                            oPerson.PrevDirection = oPerson.Direction;
                            oPerson.Direction = new Vector3(oDirection, 0);

                            oBytes = Networking.Reader.ReadByte();

                            if (oBytes == 0 || oBytes > 15)
                            {
                                oPerson.PressingA = false;
                                oPerson.PressingB = false;
                                oPerson.PressingX = false;
                                oPerson.PressingY = false;
                            }

                            var oRemainder = oBytes % 2;
                            oPerson.PressingA = (oRemainder == 1);
                            oBytes /= 2;
                            oPerson.PressingB = (oRemainder == 1);
                            oBytes /= 2;
                            oPerson.PressingX = (oRemainder == 1);
                            oBytes /= 2;
                            oPerson.PressingY = (oRemainder == 1);
                        }
                    }
                }
            }
        }

        protected abstract void ClientReceiveFromHost();
        //{
        //foreach (var oLocal in Networking.Session.LocalGamers)
        //{
        //    if (oLocal.IsHost == false)
        //    {
        //        while (oLocal.IsDataAvailable)
        //        {
        //            NetworkGamer oHost;
        //            oLocal.ReceiveData(Networking.Reader, out oHost);

        //            foreach (var oGamer in Networking.Session.AllGamers)
        //            {
        //                var oPerson = oGamer.Tag as Person3D;
        //                var oPositionDestination = oPerson.PositionDestination;
        //                oPositionDestination.X = Networking.Reader.ReadSingle();
        //                oPositionDestination.Y = Networking.Reader.ReadSingle();
        //var oRotation = oPerson.Rotation;
        //oRotation.Z = Networking.Reader.ReadInt16();

        //                oPerson.PrevPosition = oPerson.Position;
        //                oPerson.PositionDestination = oPositionDestination;
        //                oPerson.Rotation = oRotation;
        //                var oCondition = (ConditionName)Networking.Reader.ReadByte();
        //                if (oPerson.Condition.Name != oCondition)
        //                {
        //                    oPerson.Condition.ChangeTypeForClient(oCondition);
        //                    oPerson.StartCondition(false);
        //                }
        //            }
        //        }
        //    }
        //}
        //}

        #endregion

        #region Helper Methods

        protected virtual void CheckForPause()
        {
            if (All.Just3DTest)
                return;

            var oPressedStart = false;

            var oInputKey = InputManager.Keyboard;
            if (oInputKey.JustPressed(Keys.Enter))
                oPressedStart = true;

            var oInputPad = InputManager.GamePad1;
            if (oInputPad.JustPressed(Buttons.Start))
                oPressedStart = true;
        }

        protected static void AddObject(object oObject)
        {
            var oSkater = oObject as Skater;
            var oGoalie = oObject as Goalie;
            var oPerson = oObject as Person3D;
            var oMoveable = oObject as Moveable3D;
            var oModel = oObject as DrawableModel;

            if (oSkater != null)
            {
                if (m_oSkaters == null)
                {
                    m_oSkaters = new List<Skater>();
                }
                m_oSkaters.Add(oSkater);
            }
            if (oGoalie != null)
            {
                if (m_oGoalies == null)
                {
                    m_oGoalies = new List<Goalie>();
                }
                m_oGoalies.Add(oGoalie);
            }
            if (oPerson != null)
            {
                if (m_oPeople == null)
                {
                    m_oPeople = new List<Person3D>();
                }
                m_oPeople.Add(oPerson);
            }
            if (oMoveable != null)
            {
                if (m_oMoveables == null)
                {
                    m_oMoveables = new List<Moveable3D>();
                }
                m_oMoveables.Add(oMoveable);
            }
            if (oModel != null)
            {
                if (m_oModels == null)
                {
                    m_oModels = new List<DrawableModel>();
                }
                m_oModels.Add(oModel);
            }
        }

        protected static void RemoveObject(object oObject)
        {
            var oPerson = oObject as Person3D;
            var oMoveable = oObject as Moveable3D;
            var oModel = oObject as DrawableModel;

            if (oPerson != null)
            {
                if (m_oPeople != null && m_oPeople.Contains(oPerson))
                {
                    m_oPeople.Remove(oPerson);
                }
            }
            if (oMoveable != null)
            {
                if (m_oMoveables == null && m_oMoveables.Contains(oMoveable))
                {
                    m_oMoveables.Remove(oMoveable);
                }
            }
            if (oModel != null)
            {
                if (m_oModels == null && m_oModels.Contains(oModel))
                {
                    m_oModels.Remove(oModel);
                }
            }
        }

        protected virtual void HandleCollision(Moveable3D oMoveA, Moveable3D oMoveB)
        {
            if (oMoveA.CollidesWith(oMoveB))
            {
                var oPosA = oMoveA.Position;
                var oPosB = oMoveB.Position;
                var oDelta = oPosB - oPosA;

                if (oDelta == Vector3.Zero)
                    return;

                var oNormalAB = Vector3.Normalize(oDelta);
                var oNormalBA = Vector3.Negate(oNormalAB);
                var oVeloA = oMoveA.Velocity;
                var oVeloB = oMoveB.Velocity;

                oMoveA.Velocity = Vector3.Reflect(oVeloA, oNormalBA) + oNormalBA * oVeloB.Length() * 0.5f;
                oMoveB.Velocity = Vector3.Reflect(oVeloB, oNormalAB) + oNormalAB * oVeloA.Length() * 0.5f;
            }
        }

        protected virtual void MoveMoveables()
        {
            foreach (var oMoveable in m_oMoveables)
            {
                oMoveable.Move();
            }
        }

        protected virtual void UpdateConditions()
        {
            foreach (var oPerson in People)
            {
                oPerson.UpdateCondition(this);
            }
        }

        protected virtual void MoveableCollision()
        {
            for (int i = 0; i < m_oMoveables.Count - 1; i++)
            {
                var oMoveableA = m_oMoveables[i];
                //if (oMoveableA.CanReactToCollision == false) continue;

                for (int j = i + 1; j < m_oMoveables.Count; j++)
                {
                    var oMoveableB = m_oMoveables[j];
                    if (oMoveableB.CanReactToCollision == false) continue;

                    HandleCollision(m_oMoveables[i], m_oMoveables[j]);
                }
            }
        }

        protected virtual void ArenaCollision()
        {
            foreach (var oMoveable in m_oMoveables)
            {
                oMoveable.CollisionReactionWith(m_oRink);
            }
        }

        public virtual void ExitMatch()
        {
            All.DestinationScreenManager = new MenuSystemManager(_Manager.Game);
            _Manager.RemoveAllScreens();

            Networking.Reset();
        }

        private void InsertSkater(Game oGame, ControllingPlayer oController, Alliance oAlliance)
        {
            AddObject(new Skater(oGame, oController, oAlliance));
            ControllingPlayers.Add(oController);
        }

        private void InitializeGameState()
        {
            switch (HockeyMatchState)
            {
                case GameState.Introduction:
                    {
                        HaltInput = true;
                        DetectCollision = false;
                        SetIntroductionPath();
                        break;
                    }
                case GameState.Playing:
                    {
                        HaltInput = false;
                        DetectCollision = true;
                        break;
                    }
                case GameState.FaceOff:
                    {
                        HaltInput = true;
                        DetectCollision = false;
                        SetUpFaceOffPositions();
                        ResetPositions();
                        break;
                    }
                case GameState.GoalScoredHome:
                    {
                        HaltInput = false;
                        DetectCollision = true;
                        break;
                    }
                case GameState.GoalScoredAway:
                    {
                        HaltInput = false;
                        DetectCollision = true;
                        break;
                    }
                case GameState.Intermission:
                    {
                        HaltInput = true;
                        DetectCollision = true;
                        break;
                    }
                case GameState.Conclusion:
                    {
                        HaltInput = true;
                        DetectCollision = false;
                        break;
                    }
            }
        }

        private void SetIntroductionPath()
        {
            var oHomeSkaters =
                (from s in Skaters
                 where s.TeamSide == Alliance.Home
                 select s).ToArray();
            var oAwaySkaters =
                (from s in Skaters
                 where s.TeamSide == Alliance.Away
                 select s).ToArray();

            Vector3 oPosition = Vector3.Up * 30;

            foreach (var oSkater in oHomeSkaters)
            {
                oSkater.SetStartPosition(oPosition);
                oSkater.Velocity = Vector3.Zero;
                oSkater.Direction = Vector3.Down;
            }

            foreach (var oSkater in oAwaySkaters)
            {
                oSkater.SetStartPosition(oPosition);
                oSkater.Velocity = Vector3.Zero;
                oSkater.Direction = Vector3.Down;
            }
        }

        private void ResetPositions()
        {
            foreach (var oPerson in People)
            {
                oPerson.Position = oPerson.OriginalPosition;
                oPerson.Velocity = Vector3.Zero;
                oPerson.Rotation = Vector3.Zero;
                oPerson.UpdateRotation(false, true);
            }
        }

        private void SetUpFaceOffPositions()
        {
            float fDeltaSpace = 10;

            Vector3 oFaceOffAreaLeft = Vector3.Left * fDeltaSpace;
            Vector3 oFaceOffAreaRight = Vector3.Right * fDeltaSpace;
            Vector3 oBackUpLeft = Vector3.Left * fDeltaSpace * 2;
            Vector3 oBackUpRight = Vector3.Right * fDeltaSpace * 2;

            Random oRandom = new Random();
            float fPercentX, fPercentY;

            fPercentX = ((float)oRandom.NextDouble() * 2) - 1;
            fPercentY = ((float)oRandom.NextDouble() * 2) - 1;
            oFaceOffAreaLeft.X += fPercentX * 2;
            oFaceOffAreaLeft.Y += fPercentY * 2;

            fPercentX = ((float)oRandom.NextDouble() * 2) - 1;
            fPercentY = ((float)oRandom.NextDouble() * 2) - 1;
            oFaceOffAreaRight.X += fPercentX * 2;
            oFaceOffAreaRight.Y += fPercentY * 2;

            fPercentY = ((float)oRandom.NextDouble() * 2) - 1;
            oBackUpLeft.Y = 10 * fPercentY;

            fPercentY = ((float)oRandom.NextDouble() * 2) - 1;
            oBackUpRight.Y = 10 * fPercentY;

            var oHomeSkaters =
                (from s in Skaters
                 where s.TeamSide == Alliance.Home
                 select s).ToArray();

            var oAwaySkaters =
                (from s in Skaters
                 where s.TeamSide == Alliance.Away
                 select s).ToArray();

            bool bSetUpFaceOffPerson;
            bSetUpFaceOffPerson = false;
            foreach (var oHome in oHomeSkaters)
            {
                if (bSetUpFaceOffPerson)
                {
                    oHome.SetStartPosition(oBackUpLeft);
                }
                else
                {
                    oHome.SetStartPosition(oFaceOffAreaLeft);
                    bSetUpFaceOffPerson = true;
                }
                oHome.LastDirection = Vector3.Right;
                oHome.UpdateRotation(false, true);
            }

            bSetUpFaceOffPerson = false;
            foreach (var oAway in oAwaySkaters)
            {
                if (bSetUpFaceOffPerson)
                {
                    oAway.SetStartPosition(oBackUpRight);
                }
                else
                {
                    oAway.SetStartPosition(oFaceOffAreaRight);
                    bSetUpFaceOffPerson = true;
                }
                oAway.LastDirection = Vector3.Left;
                oAway.UpdateRotation(false, true);
            }

            foreach (var oGoalie in Goalies)
            {
                oGoalie.Position = oGoalie.OriginalPosition;

                if (oGoalie.TeamSide == Alliance.Home)
                {
                    oGoalie.LastDirection = Vector3.Right;
                }
                else
                {
                    oGoalie.LastDirection = Vector3.Left;
                }
                oGoalie.UpdateRotation(false, true);
            }

            ResetPuck();
        }

        private void ResetPuck()
        {
            if (m_oPuckHolder != null)
            {
                m_oPuckHolder.HeldObjects.Clear();
            }
            if (m_oPuck != null)
            {
                m_oPuck.SetStartPosition(Vector3.Zero);
                m_oPuck.Velocity = Vector3.Zero;
                m_oPuck.Rotation = Vector3.Zero;
                m_oPuck.UpdateRotation(false, true);
            }
        }

        #endregion

        #region Properties

        public List<Person3D> People
        {
            get { return m_oPeople; }
        }

        public List<Skater> Skaters
        {
            get { return m_oSkaters; }
        }

        public List<Goalie> Goalies
        {
            get { return m_oGoalies; }
        }

        public List<Moveable3D> Moveables
        {
            get { return m_oMoveables; }
        }

        public List<ControllingPlayer> ControllingPlayers
        {
            get { return m_oControllingPlayers; }
        }

        public Camera3D Camera
        {
            get { return m_oCamera; }
        }

        public GameState HockeyMatchState
        {
            get { return m_eHockeyMatchState; }
            set
            {
                m_eHockeyMatchState = value;
                InitializeGameState();
            }
        }

        public bool HaltInput
        {
            get { return m_bHaltInput; }
            set
            {
                m_bHaltInput = value;

                if (value == true)
                {
                    foreach (var oPerson in People)
                    {
                        oPerson.Direction = Vector3.Zero;
                    }
                }
            }
        }

        public bool DetectCollision
        {
            get;
            set;
        }

        public Skater HighlightSkater
        {
            get { return m_oHighlightSkater; }
            set { m_oHighlightSkater = value; }
        }

        public Puck3D Puck
        {
            get { return m_oPuck; }
        }

        public Goal3D HomeGoal
        {
            get { return m_oHomeGoal; }
        }

        public Goal3D AwayGoal
        {
            get { return m_oAwayGoal; }
        }

        public Skater PuckHolder
        {
            get { return m_oPuckHolder; }
        }

        #endregion

        #region Fields

        private Camera3D m_oCamera;
        private Grid3D m_oGrid;
        private Arena m_oArena;
        private Rink m_oRink;

        protected static List<Person3D> m_oPeople;
        protected static List<Goalie> m_oGoalies;
        protected static List<Skater> m_oSkaters;
        protected static List<Moveable3D> m_oMoveables;
        protected static List<DrawableCustom> m_oCustomModels;
        protected static List<DrawableModel> m_oModels;

        private bool m_bCameraMode;
        private bool m_bPaused;

        protected DrawableModel _PlayerIndicator;
        protected float _PlayerIndicatorRotation;

        private const float INDICATOR_ROTATION_RATE = MathHelper.Pi * 0.005f;

        private List<ControllingPlayer> m_oControllingPlayers;

        private GameState m_eHockeyMatchState;
        private bool m_bHaltInput;
        private Skater m_oHighlightSkater;

        private Goal3D m_oHomeGoal;
        private Goal3D m_oAwayGoal;
        private Puck3D m_oPuck;
        private Skater m_oPuckHolder;

        private Goalie m_oHomeGoalie;
        private Goalie m_oAwayGoalie;

        public enum GameState
        {
            Introduction,
            Playing,
            GoalScoredHome,
            GoalScoredAway,
            FaceOff,
            Intermission,
            Conclusion
        }

        #endregion

    }
}